home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Libraries / VideoToolbox 95.11.08 / Utilities / MeasureMTF / MeasureMTF.c next >
Encoding:
C/C++ Source or Header  |  1995-08-14  |  11.2 KB  |  210 lines  |  [TEXT/CWIE]

  1. /*
  2. MeasureMTF.c
  3. Copyright © 1990-1993 Denis G. Pelli
  4. Measures the modulation transfer function of a monitor. Uses drifting gratings,
  5. both horizontal and vertical, of constant temporal frequency. The results
  6. are saved in a KaleidaGraph text file, MTF?.data, where ? stands for the screen
  7. number. The data file includes the dc and second harmonic.
  8. HISTORY:
  9. 10/11/90    dgp    wrote it.
  10. 10/12/90    dgp    added dc and harmonics.
  11. 3/18/91        dgp    updated to work with new PlotXY, but not tested
  12. 4/15/91        dgp Check for NewPaletteManager().
  13. 8/24/91        dgp    Made compatible with THINK C 5.0.
  14. 8/27/92    dgp    replace SysEnvirons() by Gestalt()
  15. 10/23/92 dgp try to read latest LuminanceRecord
  16. 2/7/93    dgp    updated to use SetPixelsQuickly.
  17. */
  18. #include "VideoToolbox.h"
  19. #include "Luminance.h"
  20. //#include <Packages.h>
  21. #if (THINK_C || THINK_CPLUS || SYMANTEC_C)
  22.     #include <profile.h>    /* for timing */
  23.     #define SYMANTEC_C_PROFILE 1
  24. #endif
  25. #define TWO_PI (2.0*PI)
  26. #define MAX(a,b) ((a)>(b)?(a):(b))
  27. #define MIN(a,b) ((a)<(b)?(a):(b))
  28.  
  29. void MeasureMTF(void);
  30. double MeasureContrast(GDHandle device,LuminanceRecord *LP,int tPeriod,double c[10],WindowPtr plotWindow);
  31. double saw(double x);
  32.  
  33. void main(void)
  34. {
  35.     assert(StackSpace()>1000);
  36.     StackGrow(30000);
  37.     Require(gestalt8BitQD);
  38.     MeasureMTF();
  39. }
  40.  
  41. void MeasureMTF(void)
  42. {
  43.     GDHandle device,oldGDHandle;
  44.     CWindowPtr window;
  45.     WindowPtr plotWindow,mtfWindow;
  46.     LuminanceRecord LR;
  47.     char string[100],string2[100],outName[100];
  48.     unsigned long seconds;
  49.     FILE *outfile[2];
  50.     int i,tPeriod,width;
  51.     Rect r,srcRect,dstRect;
  52.     double a,b,f,fNominal,c,L,cOut,cH[10],cV[10];
  53.     PlotXYStyle mtfStyle[2];
  54.     unsigned long row[1048];    // hopefully long enough for any video device
  55.     short oldScreen;
  56.  
  57.     assert(StackSpace()>4000);
  58.     MaximizeConsoleHeight();
  59.     #if (THINK_C || THINK_CPLUS || SYMANTEC_C)
  60.         console_options.ncols=100;
  61.         printf("\n");                       /* Initialize QuickDraw */
  62.     #else
  63.         InitGraf(&qd.thePort);
  64.         InitFonts();
  65.         InitWindows();
  66.         InitCursor();
  67.     #endif
  68.     #if SYMANTEC_C_PROFILE
  69.         InitProfile(200,1);
  70.         _profile=0;    /* disable profiling for the moment */
  71.     #endif
  72.     printf("Welcome to MeasureMTF.\n");
  73.     #include "LuminanceRecord1.h"                    // read at compile time
  74.     oldScreen=LR.screen;
  75.     if(GetScreenDevice(1)!=NULL){
  76.         printf("Which screen would you like to calibrate (%d):",LR.screen);
  77.         gets(string);
  78.         sscanf(string,"%d",&LR.screen);
  79.     }
  80.     else LR.screen=0;
  81.     sprintf(string,"LuminanceRecord%d.h",LR.screen);
  82.     i=ReadLuminanceRecord(string,&LR,0);        // try to read latest LuminanceRecord
  83.     if(i<1)printf("Warning: couldn't find “%s”. Calibrating screen %d.\n"
  84.         ,string,LR.screen);
  85.     else oldScreen=LR.screen;
  86.  
  87.     mtfStyle[0].continuing=0;
  88.     mtfStyle[0].lineWidth=1;
  89.     mtfStyle[0].symbolWidth=0;
  90.     mtfStyle[0].dash[0]=0;
  91.     mtfStyle[0].dashOffset=0;
  92.     mtfStyle[0].color=blackColor;
  93.     mtfStyle[1]=mtfStyle[0];
  94.     mtfStyle[1].color=blueColor;
  95.     
  96.     GetDateTime(&seconds);
  97.     sprintf(outName,"MTF%d.%s",LR.screen,DatedString(seconds));
  98.     outfile[0]=stdout;
  99.     outfile[1]=fopen(outName,"w");
  100.     if(outfile[1]==NULL) PrintfExit("Sorry, can't create \"%s\".\007\n",outName);
  101.     SetFileInfo(outName,'TEXT','QKPT');    /* for Kaleidagraph */
  102.  
  103.     /* Find device corresponding to the experimental screen. */
  104.     oldGDHandle=GetGDevice();
  105.     device = GetScreenDevice(LR.screen);
  106.     if(NewPaletteManager())
  107.         SetDepth(device,8,1,1);    /* 8-bit pixels, color mode */
  108.     window = GDOpenWindow(device);
  109.     
  110.     if(oldScreen==LR.screen){
  111.         printf("Luminance was calibrated on %s\n",LR.date);
  112.         printf("at %.2f %s by %s\n",LR.LBackground,LR.units,LR.notes);
  113.         printf("Range is %.1f to %.1f %s.\n",LR.LMin,LR.LMax,LR.units);
  114.     }
  115.     L=LR.LBackground;    /* desired mean luminance */
  116.     printf("Enter desired display luminance in cd/m^2 (%.2f):",L);
  117.     fgets(string,sizeof(string)-1,stdin);
  118.     sscanf(string,"%lf", &L);
  119.  
  120.     c=0.9;
  121.     c=MIN(c,MIN((LR.LMax-L)/L,(L-LR.LMin)/L));
  122.     printf("Contrast %.1f\n",c);
  123.     printf("Computing sinusoidal lookup table . . .\n");
  124.     for(i=0;i<256;i++){
  125.         SetLuminance(device,&LR,i,L*(1.0+c*sin(TWO_PI*i/256.0)),L*(1.0-c),L*(1.0+c));
  126.     }
  127.  
  128.     tPeriod=64;                                    /* frames in one temporal period */
  129.     width=window->portRect.right;                /* pixels across screen */
  130.     SetRect(&r,0,0,2*tPeriod,2*tPeriod);
  131.     OffsetRect(&r,64,64);
  132.     plotWindow=NewWindow(NULL,&r,"\pL",1,noGrowDocProc,(WindowPtr) -1L,FALSE,0L);
  133.     SetRect(&r,0,0,192,192);
  134.     OffsetRect(&r,640-r.right-64,64);
  135.     mtfWindow=NewWindow(NULL,&r,"\pMTF",1,noGrowDocProc,(WindowPtr) -1L,FALSE,0L);
  136.     MeasureContrast(device,&LR,tPeriod,cH,plotWindow);        /* measure vBlack */
  137.     ffprintf(outfile,
  138.         "notes" "\tcycles/pixel" "\tcycles/screen" 
  139.         "\tHoriz. Grating Gain" "\tVert. Grating Gain" "\tVert./Horiz.");
  140.     fprintf(outfile[0],"\n");
  141.     fprintf(outfile[1],"\tc" "\tcH[0]" "\tcH[1]" "\tcH[2]" "\tcV[0]" "\tcV[1]" "\tcV[2]" 
  142.         "\t(cV[0]-cH[0])/cH[0]" "\tcV[2]/(c*V/H)^2" "\t(cV[0]-cH[0])/cH[0]/(c*V/H)^2"
  143.         "\n");
  144.     ffprintf(outfile,"notes");    /* put some text in notes column */
  145.     for(fNominal=0.0;;fNominal=MAX(fNominal*pow(2.0,1.0/2.0),fNominal+1.0/width)){
  146.         f=floor(fNominal*width+0.5)/width; /* integral periods for zero mean over line */
  147.         if(fNominal>0.5)f=0.5;
  148.         /* horizontal grating */
  149.         srcRect=dstRect=window->portRect;
  150.         dstRect.left=srcRect.right=1;
  151.         for(i=0;i<srcRect.bottom;i++){
  152.             row[0]=128.0+127.5*saw(TWO_PI*f*i);
  153.             SetWindowPixelsQuickly((WindowPtr)window,0,i,row,1);
  154.         }
  155.         /* CopyBits uses the inverse color table of the current GDevice. */
  156.         SetGDevice(device);
  157.         CopyBits((BitMap *)*window->portPixMap,(BitMap *)*window->portPixMap,
  158.             &srcRect,&dstRect,srcCopy,NULL);
  159.         SetGDevice(oldGDHandle);
  160.         cOut=MeasureContrast(device,&LR,tPeriod,cH,plotWindow);
  161.         ffprintf(outfile,"\t%10.5f" "\t%10.2f" "\t%12.6f",f,f*dstRect.right,cOut/c);
  162.         /* draw the MTF */
  163.         PlotXY(mtfWindow
  164.             ,log(f/(1./640.))/log(0.5/(1./640.)), log(cOut/c/0.3)/log(1.1/0.3)
  165.             ,&mtfStyle[0]);
  166.  
  167.         /* vertical grating */
  168.         srcRect=dstRect=window->portRect;
  169.         dstRect.bottom=srcRect.top=srcRect.bottom-1;
  170.         for(i=0;i<srcRect.right;i++)row[i]=128.0+127.5*saw(TWO_PI*f*i);
  171.         SetWindowPixelsQuickly((WindowPtr)window,0,srcRect.top,row,srcRect.right);
  172.         SetGDevice(device);
  173.         CopyBits((BitMap *)*window->portPixMap,(BitMap *)*window->portPixMap,
  174.             &srcRect,&dstRect,srcCopy,NULL);
  175.         SetGDevice(oldGDHandle);
  176.         cOut=MeasureContrast(device,&LR,tPeriod,cV,plotWindow);
  177.         ffprintf(outfile,"\t%20.6f" "\t%18.6f",cOut/c,cV[1]/cH[1]);
  178.         fprintf(outfile[0],"\n");
  179.         fprintf(outfile[1],"\t%f" "\t%f" "\t%f" "\t%f" "\t%f" "\t%f" "\t%f"
  180.             ,c,cH[0],cH[1],cH[2],cV[0],cV[1],cV[2]);
  181.         a=(cV[0]-cH[0])/cH[0];    /* dc error, expressed as a contrast */
  182.         b=cV[1]/cH[1];            /* contrast gain of video amplifier */
  183.         fprintf(outfile[1],"\t%f" "\t%f" "\t%f" "\n"
  184.             ,a,cV[2]/(c*b*c*b),a/(c*b*c*b));
  185.         /* draw the MTF */
  186.         PlotXY(mtfWindow
  187.             ,log(f/(1./640.))/log(0.5/(1./640.)), log(cOut/c/0.3)/log(1.1/0.3)
  188.             ,&mtfStyle[1]);
  189.         
  190.         if(f==0.5)break;
  191.     }
  192.     
  193.     /* print notes */
  194.     IUDateString(seconds,longDate,(unsigned char *)string);
  195.     p2cstr((unsigned char *)string);
  196.     IUTimeString(seconds,FALSE,(unsigned char *)string2);
  197.     p2cstr((unsigned char *)string2);
  198.     ffprintf(outfile,"%s %s\n",string2,string);
  199.     ffprintf(outfile,"%.1f Hz\n",1.0/(tPeriod*0.015));
  200.     ffprintf(outfile,"%.2f cd/m^2\n",L);
  201.     ffprintf(outfile,"%.3f contrast\n",c);
  202.     ffprint saw(double x)
  203. /* returns sawtooth function with same phase, amplitude, and symmetry as sin() */
  204. {
  205.     x/=TWO_PI;
  206.     x-=0.5;
  207.     x-=floor(x);
  208.     return 2.0*x-1.0;
  209. }
  210.